/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package sync.services;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import org.apache.http.entity.ContentProducer;
import sync.model.Assignment;
import sync.model.AssignmentItem;

/**
 *
 * @author yarden
 */
public class AssignmentsParser {
    public static final String ASSIGNMENT_TAG       = "assignment";
    public static final String COMPLETED_TAG        = "completed";
    public static final String CONTEXT_TAG          = "context";
    public static final String DATE_ASSIGNED_TAG    = "dateAssigned";
    public static final String DATE_COMPLETED_TAG   = "dateCompeted";
    public static final String DATE_MODIFIED_TAG    = "dateModified";
    public static final String GET_ASSIGNMENT       = "getAssignments";
    public static final String ITEM_TAG             = "unitOfAnalysisAssignment";
    public static final String NAME_TAG             = "name";
    public static final String SCHEMA_TAG           = "schema";
    public static final String TODO_TAG             = "todo";
    public static final String USER_TAG             = "user";
    
    public static final String ID_ATTRIBUTE         = "uid";

    
    private XMLEventReader reader;
    private XMLEvent event;
    
    
    /**
     * Parse an assignments reply from a server
     * @param input an XML input stream reply form the server
     * @return a list of Assignments
     */
    public List<Assignment> parseAssignments(InputStream input) {
        List<Assignment> list = new ArrayList<Assignment>();
        
        XMLInputFactory factory = getFactory();
        
        try {
            reader = factory.createXMLEventReader(input);
            
            // verify this is a response to a getAssignments 
            if (!verify(GET_ASSIGNMENT)) {
                 Logger.getLogger(AssignmentsParser.class.getName()).log(Level.SEVERE, "Server response to the wrong request");
            }
            else {
                while (reader.hasNext()) {
                    list.add(readAssignment());
                }
            }
        } catch (XMLStreamException e) {
            Logger.getLogger(AssignmentsParser.class.getName()).log(Level.SEVERE, "While reading server response", e);
        }
        
        return list;
    }
    
    public ContentProducer getContentProcuder(final List<AssignmentItem> items) {
        return new ContentProducer() {
            public void writeTo(OutputStream outstream) throws IOException {
                Writer writer = new OutputStreamWriter(outstream, "UTF-8");
                
                writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
                writer.write("<submitAssignments method=\"request\">");
                for (AssignmentItem item : items) {
                    if (item.getStatus() == Assignment.Status.Done) {
                        writer.write("  <assignment uid=\""+item.getAssignment().getId()+"\">");
                        writer.write("    <completed>");
                        writer.write("      <unitOfAnalysisAssignment uid=\""+item.getId()+"\">");
                        writer.write("          <dateCompleted>"+item.getCompleted()+"</dateCompleted>");
                        writer.write("      </unitOfAnalysisAssignment>");
                        writer.write("    </completed>");
                        writer.write("  </assignment>");
                    }
                }
                writer.write("</submitAssignments>");
                writer.flush();
            }
        };
    }
    
    private Assignment readAssignment() throws XMLStreamException {
        Assignment assignment = null;
        
        while (reader.hasNext()) {
            event = reader.nextEvent();
            if (event.isStartElement() && event.asStartElement().getName().getLocalPart().equals(ASSIGNMENT_TAG)) {
                String id = event.asStartElement().getAttributeByName(new QName(ID_ATTRIBUTE)).getValue();
                assignment = new Assignment(id);
                while (reader.hasNext()) {
                    event = reader.nextEvent(); 
                    if (event.isStartElement()) {
                        StartElement elem = event.asStartElement();
                        String tagName = elem.getName().getLocalPart();
                       
                        if (tagName.equals(USER_TAG)) {
                            assignment.setUser(elem.getAttributeByName(new QName(ID_ATTRIBUTE)).getValue());
                        }
                        else if (tagName.equals(DATE_ASSIGNED_TAG)) {
                            getEvent();
                            if (event.isCharacters())
                                assignment.setAssigned(event.asCharacters().toString());
                            skipToEnd(DATE_ASSIGNED_TAG);
                        }
                        else if (tagName.equals(DATE_COMPLETED_TAG)) {
                            getEvent();
                            if (event.isCharacters())
                                assignment.setCompleted(event.asCharacters().toString());
                            skipToEnd(DATE_COMPLETED_TAG);
                        } 
                        else if (tagName.equals(DATE_MODIFIED_TAG)) {
                            getEvent();
                            if (event.isCharacters())
                                assignment.setModified(event.asCharacters().toString());
                            skipToEnd(DATE_MODIFIED_TAG);
                        } 
                        else if (tagName.equals(SCHEMA_TAG)) {
                            assignment.setSchemaId(elem.getAttributeByName(new QName(ID_ATTRIBUTE)).getValue());
                            getEvent();
                            if (event.isCharacters())
                                assignment.setSchema(event.asCharacters().toString());
                            skipToEnd(SCHEMA_TAG);
                        }
                        else if (tagName.equals(ITEM_TAG)) {
                                assignment.addItem(readAssignmentItem());
                        }
                        /* ignore for now 
                        else if (tagName.equals(COMPLETED_TAG)) {
                        }
                        else if (tagName.equals(CONTEXT_TAG)) {
                            
                        }
                        */
                    }
                    
                }
            }
        }
        return assignment;
    }
    
    private AssignmentItem readAssignmentItem() throws XMLStreamException {
        AssignmentItem item = null;
        
       if (isStartTag(ITEM_TAG)) {
            String id = event.asStartElement().getAttributeByName(new QName(ID_ATTRIBUTE)).getValue();
            item = new AssignmentItem(id);
            
            for (getEvent(); !isEndTag(ITEM_TAG); getEvent()) {
                if (event.isStartElement()) {
                    StartElement start = event.asStartElement();
                    String tagName = start.getName().getLocalPart();
                    if (tagName.equals(NAME_TAG)) {
                        getEvent();
                        item.setName(event.asCharacters().toString());
                        skipToEnd(NAME_TAG);
                    }
                }
            }
        }
    
        return item;
    } 
    
    private boolean verify(String tag) throws XMLStreamException {
        while (reader.hasNext()) {
            event = reader.nextEvent();
            if (event.isStartElement()) {
                //System.out.println(event);
                return event.asStartElement().getName().getLocalPart().equals(tag);
                    
            }
        }
        return false;
    }

    private void getEvent() throws XMLStreamException {
        event = reader.nextEvent();
    }
    
    private boolean isStartTag(String tag) {
        return event.isStartElement() && event.asStartElement().getName().getLocalPart().endsWith(tag);
    }
    
    private boolean isEndTag(String tag) {
        return event.isEndElement() && event.asEndElement().getName().getLocalPart().equals(tag);
    }
    
    private void skipToEnd(String tag) throws XMLStreamException {
        while (!isEndTag(tag) && reader.hasNext()) {
            getEvent();
        }
    }
    
    private XMLInputFactory getFactory() {
        XMLInputFactory factory = null;
        try {
            factory = XMLInputFactory.newInstance();
            factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.TRUE);
            factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
            factory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.TRUE);
            factory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE);
        } catch (Exception e) {
            Logger.getLogger(AssignmentsParser.class.getName()).log(Level.SEVERE, null, e);
        }
        return factory;
    }
}
